import { HttpException, Injectable } from '@nestjs/common';
import { CreateRoleDto, FindRoleDto, UpdateRoleDto, RemoveRoleDto, CreateBatchRoleDto, RemoveBatchRoleDto, FindPermissionByRoleDto } from './dto/role.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { In, Repository } from 'typeorm';
import { Role } from './entities/role.entity';
import { plainToInstance } from 'class-transformer';
import { StatusEnum } from '@/common/enums';
import { isTruthy } from '@/common/utils/judge';

@Injectable()
export class RoleService {
  constructor(
    @InjectRepository(Role)
    private readonly role_db: Repository<Role>
  ) {}

  async create(data: CreateRoleDto) {
    const entity = plainToInstance(Role, data);
    const res = await this.role_db.save(entity);

    return {
      data: res,
      msg: '新增成功'
    };
  }

  async findAll(data: FindRoleDto) {
    const queryBuilder = this.role_db.createQueryBuilder('role');

    // 查询条件
    if (data.id) queryBuilder.andWhere('role.id = :id', { id: data.id });
    if (data.name) queryBuilder.andWhere('role.name = :name', { name: data.name });
    if (data.label) queryBuilder.andWhere('role.label LIKE :label', { label: `%${data.label}%` });
    // 数字类型特殊处理
    if (isTruthy(data.status, 'zero')) queryBuilder.andWhere('role.status = :status', { status: String(data.status) });

    // 添加排序
    queryBuilder.orderBy(`role.${data.sortField}`, data.sortOrder);
    // pageSize 为正整数时，添加分页
    data.pageSize > 0 && queryBuilder.skip((data.pageNum - 1) * data.pageSize).take(data.pageSize);
    // 执行查询
    const [res, total] = await queryBuilder.getManyAndCount();
    // 总页数
    const pages = data.pageSize > 0 ? Math.ceil(total / data.pageSize) : 1;

    return {
      data: res,
      pageNum: data.pageNum,
      pageSize: data.pageSize,
      pages,
      total,
      msg: '查询成功'
    };
  }

  async findOne(data: FindRoleDto) {
    const queryBuilder = this.role_db.createQueryBuilder('role');

    // 必须指定查询条件
    if (!data.id && !data.name) {
      return {
        code: 400,
        msg: '查询条件不能为空'
      };
    }

    // 只查询主键或唯一键
    if (data.id) queryBuilder.andWhere('role.id = :id', { id: data.id });
    if (data.name) queryBuilder.andWhere('role.name = :name', { name: data.name });

    // 执行查询
    const res = await queryBuilder.getOne();

    return {
      data: res,
      msg: '查询成功'
    };
  }

  /**
   * 根据角色查询权限 [该api仅限admin角色使用]
   * @param data 参数
   * @property {string[]} data.roles 角色name数组
   */
  async findPermissionByRole(data: FindPermissionByRoleDto) {
    const { roles } = data;

    if (!roles?.length) {
      return {
        code: 400,
        msg: '无有效角色字段'
      };
    }

    // 执行查询
    const res = await this.role_db.find({
      where: {
        name: In(data.roles),
        status: StatusEnum.Enabled
      }
    });

    // 获取有效权限并去重
    const allPermissions = res.filter((item) => item.permissions).flatMap((i) => i.permissions);
    const uniquePermissions = [...new Set(allPermissions)];

    return {
      data: uniquePermissions,
      msg: '查询成功'
    };
  }

  async update(data: UpdateRoleDto) {
    const queryBuilder = this.role_db.createQueryBuilder().update();

    // 只查询主键
    queryBuilder.where('id = :id', { id: data.id });

    // 更新数据
    queryBuilder.set(data);

    // 执行更新
    const res = await queryBuilder.execute();

    if (res.affected > 0) {
      return {
        msg: '更新成功'
      };
    } else {
      return {
        code: 400,
        msg: '无有效数据更新'
      };
    }
  }

  async remove(data: RemoveRoleDto) {
    const queryBuilder = this.role_db.createQueryBuilder().delete();

    // 只查询主键
    queryBuilder.where('id = :id', { id: data.id });

    // 执行删除
    const res = await queryBuilder.execute();

    if (res.affected > 0) {
      return {
        msg: '删除成功'
      };
    } else {
      return {
        code: 400,
        msg: '无有效数据删除'
      };
    }
  }

  async removeBatch(data: RemoveBatchRoleDto) {
    let { ids } = data;
    const mainkey = 'id'; // 主键
    let clash = []; // 冲突的主键明细
    let romoved = 0; // 删除成功的数量

    try {
      await this.role_db.manager.transaction(async (manager) => {
        if (ids.length == 0) {
          throw new Error('ids 为空，无数据删除');
        }

        const repManager = manager.getRepository(Role);
        const deleteRes = await repManager.delete({ id: In(ids) });
        romoved = deleteRes.affected;

        if (romoved == 0) {
          throw new Error('无有效数据删除');
        }

        // 如果有未受影响的行，说明有些ID删除失败
        if (ids.length !== romoved) {
          // 查询仍存在的ID
          const remains = await repManager.find({ where: { [mainkey]: In(ids) } });
          clash = remains.map((item) => item[mainkey]);

          if (clash.length > 0) {
            // 抛出异常，阻止整个事务的提交
            throw new Error(`删除失败 ids: ${clash.join(',')}`);
          }
        }
      });

      return {
        msg: '批量删除成功',
        tip: romoved
      };
    } catch (error) {
      return {
        code: 400,
        errMsg: error.message,
        msg: '批量删除失败'
      };
    }
  }

  async createBatch(data: CreateBatchRoleDto) {
    let { list, cover } = data;
    const mainkey = 'name'; // 决策键
    let clash = []; // 冲突的决策键明细
    let total = 0; // 新增成功的数量
    try {
      await this.role_db.manager.transaction(async (manager) => {
        const repManager = manager.getRepository(Role);

        // 提取原始list中所有项的决策键
        const mainkeys = list.map((item) => item[mainkey]);
        // 根据决策键查询所有已存在的项
        const exists = await repManager.find({ where: { [mainkey]: In(mainkeys) } });
        // 所有冲突的决策键
        clash = exists.map((item) => item[mainkey]);
        // list中的所有冲突项（覆盖添加时用于更新处理）
        const existlist = list.filter((item) => clash.some((it) => it === item[mainkey]));
        // list中需要增量添加的项
        const addlist = list.filter((item) => !clash.some((it) => it === item[mainkey]));

        // 增量添加
        if (addlist.length > 0) {
          await repManager.save(addlist);
          total += addlist.length; // 追加增量添加数量
        }

        // 覆盖添加 即更新冲突项
        if (cover && clash.length > 0) {
          for (const item of existlist) {
            await repManager.update({ [mainkey]: item[mainkey] }, item);
          }
          total += clash.length; // 追加覆盖添加数量
        }
      });

      return {
        msg: '批量新增成功',
        tip: clash.length > 0 ? `${mainkey}=>${clash}` : undefined,
        total
      };
    } catch (error) {
      return {
        code: 400,
        errMsg: error.message,
        msg: '批量新增失败'
      };
    }
  }
}
